package fem2d;

import fem2.AbstractStructuralFrameStaticDemo;
import fem2.Constraint;
import fem2.Debugger;
import fem2.Edge;
import fem2.Element;
import fem2.Force;
import fem2.MaterialModel;
import fem2.Mesh;
import fem2.MeshPart;
import fem2.Model;
import fem2.Node;
import fem2.analysis.Analysis;
import fem2.element.CrisfieldTrusssElement;
import fem2.enu.EchoLevelType;
import fem2.enu.State;
import fem2.material.StVenantKirchhoffMM;
import fem2.observer.DisplacementObserver;
import fem2.observer.TimeStepObserver;
import fem2.observer.XYGraphicalObserver;
import fem2.observer.XYSourceObserver;
import fem2.strategies.ArcLengthSolutionStrategyWithSphereConstraint;
import fem2.strategies.Strategy;

public class TwoBarSnapthroughProblem extends AbstractStructuralFrameStaticDemo {

	@Override
	public Mesh createMesh() {
		double l = 5;
		double h = 1;

		Mesh mesh = new Mesh();

		Node n1 = mesh.addNode(new Node(0, 0));
		Node n2 = mesh.addNode(new Node(l, h));
		Node n3 = mesh.addNode(new Node(2 * l, 0));

		mesh.addEdge(new Edge(n1, n2));
		mesh.addEdge(new Edge(n2, n3));

		return mesh;
	}

	@Override
	public Model createConditions(Model m) {
		Constraint c = new Constraint(false, false);

		m.getMesh().getNode(0).setConstraint(c);
		m.getMesh().getNode(2).setConstraint(c);

		Force f = new Force(m.getMesh().getNode(1), 0, -1e3);
		m.addLoad(f);

		return m;
	}

	@Override
	public MaterialModel createMaterial(Model m) {
		double E = 2.1e8;
		double nu = 0.3;
		double t = 0.1;
		return new StVenantKirchhoffMM(E, nu, t, 0.0, State.ONE_DIMENSIONAL);
	}

	@Override
	public void addObservers(final Model m, final Strategy s, TimeStepObserver o) {
		Node n1 = m.getMesh().getNode(1);
		Node n2 = m.getMesh().getNode(2);
		s.addObserver(new DisplacementObserver(n1, n2));

		XYSourceObserver xy = new XYSourceObserver() {

			@Override
			public void fireStarted() {
				double lambda = s.getCurrentTimeStep();
				double u = m.getMesh().getNode(1).getUHatLocal()[1];
				setData(-u, lambda);
			}

			@Override
			public void fireTimeStepStarted() {
			}

			@Override
			public void fireIterationStarted() {
			}

			@Override
			public void fireIterationFinished() {
			}

			@Override
			public void fireTimeStepFinished() {
				double lambda = s.getCurrentTimeStep();
				double u = m.getMesh().getNode(1).getUHatLocal()[1];
				setData(-u, lambda);
			}

			@Override
			public void fireFinished() {
			}
		};

		s.addObserver(xy);
		s.addObserver(new XYGraphicalObserver(xy, "load-displacement curve"));
	}

	public void run() {
		Model m = createModel();

		// Analysis an = new LinearStaticAnalysis(m);
		// an.getStrategy()
		// .setEchoLevel(EchoLevelType.OUTPUT, EchoLevelType.AUX1);
		// an.getStrategy().setSolverType(SolverType.BAITSCH_SOLVER_GENERIC);
		// an.getStrategy().setMaxNt(100);
		// addObservers(m, an.getStrategy());
		//
		// an.run();

		Analysis an = new Analysis(m);
		Strategy s = new ArcLengthSolutionStrategyWithSphereConstraint(m, 0.05, 0.0);
		s.setNumberOfTimeStep(45);
		s.setEchoLevel(EchoLevelType.OUTPUT, EchoLevelType.DEBUG);
		s.setMaxNt(50);
		an.setStrategy(s);

		addObservers(m, s, null);

		an.run();

		// postProcess(m);

		Debugger.warn("Analysis completed");
	}

	@Override
	public Element createElement(MeshPart mp, MaterialModel mm) {
		return new CrisfieldTrusssElement(mp, mm, 0.0);
	}

	public static void main(String[] args) {
		AbstractStructuralFrameStaticDemo demo = new TwoBarSnapthroughProblem();

		demo.run();
	}
}
